/*
 *  Copyright 2011 yura.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *  under the License.
 */

package org.opu.vcforms.transform_t2d.text;

import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Properties;
import org.opu.vcforms.datasource.DataReceiver;
import org.opu.vcforms.datasource.Storage;
import org.opu.vcforms.template.ContentDelegator;
import org.opu.vcforms.template.Data;
import org.opu.vcforms.template.Document;
import org.opu.vcforms.template.TxtTemplate;
import org.opu.vcforms.template.VarDelegator;
import org.opu.vcforms.template.bean.Var;
import org.opu.vcforms.template.hf.Footer;
import org.opu.vcforms.template.hf.Header;
import org.opu.vcforms.template.hf.HeaderFooter;
import org.opu.vcforms.transform_t2d.MainProcessor;
import org.opu.vcforms.transform_t2d.Processor;
import org.opu.vcforms.transform_t2d.SystemConfiguration;
import org.opu.vcforms.transform_t2d.content.ContentProcessor;
import org.opu.vcforms.transform_t2d.text.sheet.LineCollector;
import org.opu.vcforms.transform_t2d.text.sheet.SheetSet;
import org.opu.vcforms.transform_t2d.var.VarProcessor;
import static org.opu.vcforms.transform_t2d.Logger.*;

/**
 *  Transform input template to output document
 * @author yura
 */
public class Transformer extends MainProcessor implements Processor<String, TxtTemplate>{

    protected Transformer(DataReceiver receiver, Map<String, Storage> userSettings,
            Properties systemSettings, ContentProcessor contentProcessor,
            SystemConfiguration systemConfiguration) {

        super(receiver, userSettings, systemSettings, contentProcessor,
                systemConfiguration);
    }

    /**
     *  Create new instance
     * @param receiver needed to send query and get request from database
     * @param userSettings can be empty (user data).
     * @param systemSettings systemSettings system settings (course, semestr, ...).
     *  If it null loads system properties - {@link System#getProperties()}.
     * @param contentProcessor content (can be null)
     */
    public Transformer(DataReceiver receiver, Map<String, Storage> userSettings,
            Properties systemSettings, ContentProcessor contentProcessor) {

        super(receiver, userSettings, systemSettings, contentProcessor);
    }

    /**
     *  Create new instance with nullable content (for main node - document).
     * @param receiver needed to send query and get request from database
     * @param userSettings can be empty (user data).
     * @param systemSettings systemSettings system settings (course, semestr, ...).
     *  If it null loads system properties - {@link System#getProperties()}.
     */
    public Transformer(DataReceiver receiver, Map<String, Storage> userSettings,
            Properties systemSettings) {

        super(receiver, userSettings, systemSettings);
    }

    public String transformDocument(Document doc) throws UnsupportedOperationException,
            IllegalArgumentException, NoSuchElementException {

        StringBuilder sb = new StringBuilder();

        out("Start process document");
        var(SPACE, "    ");

        for (Data data : doc) {
            processData(data, sb);
        }

        var(SPACE, "  ");
        out("End process document");

        return sb.toString();
    }

    @Override
    public String transform(TxtTemplate template) throws UnsupportedOperationException,
            IllegalArgumentException, NoSuchElementException {

        out("Start process ");
        var(SPACE, "  ");

        // 1 transfor document
        StringBuilder sb = new StringBuilder();
        sb.append(transformDocument(template.getDocument()));

        // 2 add header footer
        LineCollector lc = new LineCollector(getLineSeparator());

        if (template.haveHeader()){
            out("Start process headers footers");
            var(SPACE, "    ");

            sb = processHeaderFooters(lc, sb, template);

            var(SPACE, "  ");
            out("End process headers footers");
        } else {
            sb = new StringBuilder().append(lc.collect(lc.split(sb.toString())));
        }

        rm(SPACE);
        out("End process ");

        return sb.toString();
    }

    private void processData(Data data, StringBuilder sb) throws UnsupportedOperationException,
            IllegalArgumentException, NoSuchElementException {
        out(var(SPACE) + "process " + data + "{");
        Object spaceVar = var(SPACE);
        var(SPACE, spaceVar + "  ");

        if (data.isContent()) {
            ContentProcessor contProc = new ContentProcessor(contentProcessor,
                    null, ((ContentDelegator)data.getDelegator()).getContent());
            
            Transformer transformer = new Transformer(dbManager, userSettings,
                    systemSettings, contProc, configuration);

            contProc.setTransformer(transformer);

            try {
                contProc.init();
            } catch (NullPointerException ex) {
                throw new IllegalArgumentException("Can't init content processor", ex);
            }

            do { //until has steps
                for (Data d : data) {
                    transformer.processData(d, sb);
                }
            } while (contProc.hasData());
        }

        else if (data.isVar()) {
            VarProcessor vp = new VarProcessor(
                    contentProcessor, systemSettings, configuration);
            Var var = ((VarDelegator)data.getDelegator()).getVar();
            sb.append(vp.transform(var).getText());
        }

        else /* data is text */{
            sb.append(data.getText());
            out(var(SPACE) + "data is text: " + data.getText().replaceAll("\n", "\\\\n"));
        }

        var(SPACE, spaceVar);
        out(spaceVar + "}");
    }

    private StringBuilder processHeaderFooters(LineCollector lc, StringBuilder sb,
            TxtTemplate template) throws NoSuchElementException,
            UnsupportedOperationException, IllegalArgumentException {

        SheetSet set = new SheetSet(lc, sb.toString());
        Map<Integer, HeaderFooter> map = template.getHeadersFooters().asMap();
        HeaderFooter defaultHF = map.get(0);
        int p = 1;

        for (Integer hf : map.keySet()) {
            if (hf.equals(0)) { // skip default header
                continue;
            }

            configuration.setPage(p++);

            addHeader(map.get(hf), set);
        }

        if (set.needMore() && defaultHF == null) {
            throw new IllegalArgumentException("No default header when it necessary");
        }

        while (set.needMore()) {
            configuration.setPage(p++);
            addHeader(defaultHF, set);
        }

        return new StringBuilder().append(set.toString());
    }

    private void addHeader(HeaderFooter get, SheetSet set) throws NoSuchElementException,
            IllegalArgumentException, UnsupportedOperationException {

        String headerStr = null;
        String footerStr = null;

        Header header = get.getHeader();
        if (header != null) {
            Document document = header.getDocument();
            Transformer t = new Transformer(dbManager, userSettings,
                    systemSettings, null, configuration);
            headerStr = t.transformDocument(document);
        }

        Footer footer = get.getFooter();
        if (footer != null) {
            Document document = footer.getDocument();
            Transformer t = new Transformer(dbManager, userSettings,
                    systemSettings, null, configuration);
            footerStr = t.transformDocument(document);
        }

        set.resolvePage(get.getTextSize(), headerStr, footerStr);
    }

}
